package com.jinzhi.jzweb.aspect;

import com.jinzhi.api.exception.IFastApiException;
import com.jinzhi.common.annotation.Log;
import com.jinzhi.common.config.IFastConfig;
import com.jinzhi.common.dao.LogDao;
import com.jinzhi.common.domain.LogDO;
import com.jinzhi.common.service.LogService;
import com.jinzhi.common.type.EnumErrorCode;
import com.jinzhi.common.utils.*;
import com.jinzhi.jzapi.domain.UserDO;
import com.jinzhi.jzweb.annotation.OrderLog;
import com.jinzhi.jzweb.domain.JazOrderLogDO;
import com.jinzhi.jzweb.service.JazOrderLogService;
import com.jinzhi.sys.service.UserRoleService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <pre>
 * 日志切面
 * </pre>
 * <small> 2018年3月22日 | Aron</small>
 */
@Aspect
@Component
@Slf4j
@Data
@AllArgsConstructor
public class OrderLogAspect {

    private final LogDao logMapper;
    private final IFastConfig iFastConfig;


    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private LogService logService;

    @Autowired
    private JazOrderLogService jazOrderLogService;


    @Pointcut("execution(public * com.jinzhi..*.controller.*.*(..))")
    public void logController() {}

    // 新增切点表达式，匹配带有@Log注解的方法
    @Pointcut("@within(com.jinzhi.jzweb.annotation.OrderLog) || @annotation(com.jinzhi.jzweb.annotation.OrderLog)")
    public void logAnnotatedMethods() {}

    /**
     * 记录controller日志，包括请求、ip、参数、响应结果
     */
    //@Around("logController()") // 所有的Controller都会成效
    @Around("logController() && logAnnotatedMethods()") // 添加了注解的才会生效
    public Object controller(ProceedingJoinPoint point) throws Throwable {

        long beginTime = System.currentTimeMillis();
        long time = 0L;
        Object result;
        try {
            result = point.proceed();
            time = System.currentTimeMillis() - beginTime;
        }catch (Exception e){

            if(e instanceof IllegalArgumentException){
                result = Result.build(EnumErrorCode.illegalArgument.getCode(), e.getMessage());
            }else if(e instanceof IFastApiException){
                log.info("已知异常：{}, 继续抛出",e.getClass());
                result = Result.buildByErrorCode(e.getMessage());
            }else {
                log.info("未知异常：{}, 继续抛出",e.getClass());
                result = Result.build(EnumErrorCode.unknowFail.getCode(), EnumErrorCode.unknowFail.getMsg());
            }

            try {
                print(point, result, time, iFastConfig.isLogPretty());
                saveOrderLog(point, time,1);
            } catch (Exception e1) {
                log.warn("日志输出|保存错误: {}", e1.getMessage());
            }

            throw e;

        }
        try {
            print(point, result, time, iFastConfig.isLogPretty());
            saveOrderLog(point, time,0);
        } catch (Exception e) {
            log.warn("日志输出|保存错误: {}", e.getMessage());
        }

        return result;
    }

    /**
     * 日志打印
     *
     * 注意：
     * 1. 被外层拦截器拦截将不会记录
     * 2. 有些特殊情况下打印的日志与实际返回不相同，可参考全局异常处理
     * 3. 用于常规请求的日志打印，方便调试
     *
     * @param point
     * @param result
     * @param time
     * @param isLogPretty
     */
    private void print(ProceedingJoinPoint point, Object result, long time, boolean isLogPretty) {
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        UserDO sysUser = ShiroUtils.getSysUser();
        if(Objects.isNull(sysUser)){
            sysUser = new UserDO();
        }
        Long userId = sysUser.getId();
        String username = sysUser.getUsername();

        if (isLogPretty) {
            log.info("User request info  ---- {} ---- S", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN_19));
            log.info("请求接口: {} {}@{} {} {}.{}", request.getMethod(), request.getRequestURI(), userId, IPUtils.getIpAddr(request), point.getTarget().getClass().getSimpleName(), point.getSignature().getName());
//            log.info("请求参数:{}", JSONUtils.beanToJson(point.getArgs()));
            log.info("请求耗时:{} ms", time);
            log.info("请求用户:{} [{}]", userId, username);
            log.info("请求结果:{}", JSONUtils.beanToJson(result));
            log.info("------------------------------------------------ E", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN_19));
        } else {
            log.info("【请求】：{} {}@{}[{}] {} {}.{}{} (耗时 {} ms) 【返回】：{}", request.getMethod(), request.getRequestURI(), userId, username, IPUtils.getIpAddr(request), point.getTarget().getClass().getSimpleName(), point.getSignature().getName(), JSONUtils.beanToJson(point.getArgs()), time, JSONUtils.beanToJson(result));
        }
    }

    /**
     * <pre>
     * 保存日志
     * </pre>
     * <small> 2018年3月22日 | Aron</small>
     *
     * @param joinPoint
     * @param time
     */
    private void saveOrderLog(ProceedingJoinPoint joinPoint, long time,Integer status) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        JazOrderLogDO jazOrderLogDO = new JazOrderLogDO();
        OrderLog syslog = method.getAnnotation(OrderLog.class);
        if (syslog != null) {
            // 注解上的描述
            jazOrderLogDO.setOperation(syslog.value());
            jazOrderLogDO.setSystemModules(syslog.sysModule());
            jazOrderLogDO.setType(syslog.operationType());
        }
        // 请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        String params;
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        if (request != null) {
            jazOrderLogDO.setMethod(request.getMethod() + " " + request.getRequestURI());
            Map<String, String[]> parameterMap = request.getParameterMap();
            params = JSONUtils.beanToJson(parameterMap);
        } else {
            jazOrderLogDO.setMethod(className + "." + methodName + "()");
            Object[] args = joinPoint.getArgs();
            params = JSONUtils.beanToJson(args);
        }
        int maxLength = 4999;
        if (params.length() > maxLength) {
            params = params.substring(0, maxLength);
        }
        jazOrderLogDO.setParams(params);
        UserDO currUser = ShiroUtils.getSysUser();
        jazOrderLogDO.setTime((int) time);
        jazOrderLogDO.setStatus(status);
        jazOrderLogService.saveOrderLog(jazOrderLogDO,currUser,request);


    }
}
